home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / g_main.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  37.4 KB  |  1,521 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3.  
  4. #include "g_local.h"
  5.  
  6. level_locals_t    level;
  7.  
  8. typedef struct {
  9.     vmCvar_t    *vmCvar;
  10.     char        *cvarName;
  11.     char        *defaultString;
  12.     int            cvarFlags;
  13.     int            modificationCount;  // for tracking changes
  14.     qboolean    trackChange;    // track this variable, and announce if changed
  15. } cvarTable_t;
  16.  
  17. gentity_t        g_entities[MAX_GENTITIES];
  18. gclient_t        g_clients[MAX_CLIENTS];
  19.  
  20. vmCvar_t    g_gametype;
  21. vmCvar_t    g_dmflags;
  22. vmCvar_t    g_fraglimit;
  23. vmCvar_t    g_timelimit;
  24. vmCvar_t    g_capturelimit;
  25. vmCvar_t    g_friendlyFire;
  26. vmCvar_t    g_password;
  27. vmCvar_t    g_needpass;
  28. vmCvar_t    g_maxclients;
  29. vmCvar_t    g_maxGameClients;
  30. vmCvar_t    g_dedicated;
  31. vmCvar_t    g_speed;
  32. vmCvar_t    g_gravity;
  33. vmCvar_t    g_cheats;
  34. vmCvar_t    g_knockback;
  35. vmCvar_t    g_quadfactor;
  36. vmCvar_t    g_forcerespawn;
  37. vmCvar_t    g_inactivity;
  38. vmCvar_t    g_debugMove;
  39. vmCvar_t    g_debugDamage;
  40. vmCvar_t    g_debugAlloc;
  41. vmCvar_t    g_weaponRespawn;
  42. vmCvar_t    g_motd;
  43. vmCvar_t    g_syncronousClients;
  44. vmCvar_t    g_warmup;
  45. vmCvar_t    g_doWarmup;
  46. vmCvar_t    g_restarted;
  47. vmCvar_t    g_log;
  48. vmCvar_t    g_logSync;
  49. vmCvar_t    g_blood;
  50. vmCvar_t    g_podiumDist;
  51. vmCvar_t    g_podiumDrop;
  52. vmCvar_t    g_allowVote;
  53. vmCvar_t    g_teamAutoJoin;
  54. vmCvar_t    g_teamForceBalance;
  55. vmCvar_t    g_banIPs;
  56. vmCvar_t    g_filterBan;
  57.  
  58.  
  59. cvarTable_t        gameCvarTable[] = {
  60.     // don't override the cheat state set by the system
  61.     { &g_cheats, "sv_cheats", "", 0, 0, qfalse },
  62.  
  63.     // noset vars
  64.     { NULL, "gamename", GAMEVERSION , CVAR_SERVERINFO | CVAR_ROM, 0, qfalse  },
  65.     { NULL, "gamedate", __DATE__ , CVAR_ROM, 0, qfalse  },
  66.     { &g_restarted, "g_restarted", "0", CVAR_ROM, 0, qfalse  },
  67.     { NULL, "sv_mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse  },
  68.  
  69.     // latched vars
  70.     { &g_gametype, "g_gametype", "0", CVAR_SERVERINFO | CVAR_LATCH, 0, qfalse  },
  71.  
  72.     { &g_maxclients, "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse  },
  73.     { &g_maxGameClients, "g_maxGameClients", "0", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse  },
  74.  
  75.     // change anytime vars
  76.     { &g_dmflags, "dmflags", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue  },
  77.     { &g_fraglimit, "fraglimit", "20", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
  78.     { &g_timelimit, "timelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
  79.     { &g_capturelimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
  80.  
  81.     { &g_syncronousClients, "g_syncronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse  },
  82.  
  83.     { &g_friendlyFire, "g_friendlyFire", "1", CVAR_ARCHIVE, 0, qtrue  },
  84.  
  85.     { &g_teamAutoJoin, "g_teamAutoJoin", "0", CVAR_ARCHIVE  },
  86.     { &g_teamForceBalance, "g_teamForceBalance", "0", CVAR_ARCHIVE  },
  87.  
  88.     { &g_warmup, "g_warmup", "20", CVAR_ARCHIVE, 0, qtrue  },
  89.     { &g_doWarmup, "g_doWarmup", "0", CVAR_ARCHIVE, 0, qtrue  },
  90.     { &g_log, "g_log", "games.log", CVAR_ARCHIVE, 0, qfalse  },
  91.     { &g_logSync, "g_logSync", "0", CVAR_ARCHIVE, 0, qfalse  },
  92.  
  93.     { &g_password, "g_password", "", CVAR_USERINFO, 0, qfalse  },
  94.  
  95.     { &g_banIPs, "g_banIPs", "", CVAR_ARCHIVE, 0, qfalse  },
  96.     { &g_filterBan, "g_filterBan", "1", CVAR_ARCHIVE, 0, qfalse  },
  97.  
  98.     { &g_needpass, "g_needpass", "0", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse },
  99.  
  100.     { &g_dedicated, "dedicated", "0", 0, 0, qfalse  },
  101.  
  102.     { &g_speed, "g_speed", "320", 0, 0, qtrue  },
  103.     { &g_gravity, "g_gravity", "800", 0, 0, qtrue  },
  104.     { &g_knockback, "g_knockback", "1000", 0, 0, qtrue  },
  105.     { &g_quadfactor, "g_quadfactor", "3", 0, 0, qtrue  },
  106.     { &g_weaponRespawn, "g_weaponrespawn", "5", 0, 0, qtrue  },
  107.     { &g_forcerespawn, "g_forcerespawn", "20", 0, 0, qtrue },
  108.     { &g_inactivity, "g_inactivity", "0", 0, 0, qtrue },
  109.     { &g_debugMove, "g_debugMove", "0", 0, 0, qfalse },
  110.     { &g_debugDamage, "g_debugDamage", "0", 0, 0, qfalse },
  111.     { &g_debugAlloc, "g_debugAlloc", "0", 0, 0, qfalse },
  112.     { &g_motd, "g_motd", "", 0, 0, qfalse },
  113.     { &g_blood, "com_blood", "1", 0, 0, qfalse },
  114.  
  115.     { &g_podiumDist, "g_podiumDist", "80", 0, 0, qfalse },
  116.     { &g_podiumDrop, "g_podiumDrop", "70", 0, 0, qfalse },
  117.  
  118.     { &g_allowVote, "g_allowVote", "1", 0, 0, qfalse }
  119. };
  120.  
  121. int        gameCvarTableSize = sizeof( gameCvarTable ) / sizeof( gameCvarTable[0] );
  122.  
  123.  
  124. void G_InitGame( int levelTime, int randomSeed, int restart );
  125. void G_RunFrame( int levelTime );
  126. void G_ShutdownGame( int restart );
  127. void CheckExitRules( void );
  128.  
  129.  
  130. /*
  131. ================
  132. vmMain
  133.  
  134. This is the only way control passes into the module.
  135. This must be the very first function compiled into the .q3vm file
  136. ================
  137. */
  138. int vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6 ) {
  139.     switch ( command ) {
  140.     case GAME_INIT:
  141.         G_InitGame( arg0, arg1, arg2 );
  142.         return 0;
  143.     case GAME_SHUTDOWN:
  144.         G_ShutdownGame( arg0 );
  145.         return 0;
  146.     case GAME_CLIENT_CONNECT:
  147.         return (int)ClientConnect( arg0, arg1, arg2 );
  148.     case GAME_CLIENT_THINK:
  149.         ClientThink( arg0 );
  150.         return 0;
  151.     case GAME_CLIENT_USERINFO_CHANGED:
  152.         ClientUserinfoChanged( arg0 );
  153.         return 0;
  154.     case GAME_CLIENT_DISCONNECT:
  155.         ClientDisconnect( arg0 );
  156.         return 0;
  157.     case GAME_CLIENT_BEGIN:
  158.         ClientBegin( arg0 );
  159.         return 0;
  160.     case GAME_CLIENT_COMMAND:
  161.         ClientCommand( arg0 );
  162.         return 0;
  163.     case GAME_RUN_FRAME:
  164.         G_RunFrame( arg0 );
  165.         return 0;
  166.     case GAME_CONSOLE_COMMAND:
  167.         return ConsoleCommand();
  168.     case BOTAI_START_FRAME:
  169.         return BotAIStartFrame( arg0 );
  170.     }
  171.  
  172.     return -1;
  173. }
  174.  
  175.  
  176. void QDECL G_Printf( const char *fmt, ... ) {
  177.     va_list        argptr;
  178.     char        text[1024];
  179.  
  180.     va_start (argptr, fmt);
  181.     vsprintf (text, fmt, argptr);
  182.     va_end (argptr);
  183.  
  184.     trap_Printf( text );
  185. }
  186.  
  187. void QDECL G_Error( const char *fmt, ... ) {
  188.     va_list        argptr;
  189.     char        text[1024];
  190.  
  191.     va_start (argptr, fmt);
  192.     vsprintf (text, fmt, argptr);
  193.     va_end (argptr);
  194.  
  195.     trap_Error( text );
  196. }
  197.  
  198. /*
  199. ================
  200. G_FindTeams
  201.  
  202. Chain together all entities with a matching team field.
  203. Entity teams are used for item groups and multi-entity mover groups.
  204.  
  205. All but the first will have the FL_TEAMSLAVE flag set and teammaster field set
  206. All but the last will have the teamchain field set to the next one
  207. ================
  208. */
  209. void G_FindTeams( void ) {
  210.     gentity_t    *e, *e2;
  211.     int        i, j;
  212.     int        c, c2;
  213.  
  214.     c = 0;
  215.     c2 = 0;
  216.     for ( i=1, e=g_entities+i ; i < level.num_entities ; i++,e++ ){
  217.         if (!e->inuse)
  218.             continue;
  219.         if (!e->team)
  220.             continue;
  221.         if (e->flags & FL_TEAMSLAVE)
  222.             continue;
  223.         e->teammaster = e;
  224.         c++;
  225.         c2++;
  226.         for (j=i+1, e2=e+1 ; j < level.num_entities ; j++,e2++)
  227.         {
  228.             if (!e2->inuse)
  229.                 continue;
  230.             if (!e2->team)
  231.                 continue;
  232.             if (e2->flags & FL_TEAMSLAVE)
  233.                 continue;
  234.             if (!strcmp(e->team, e2->team))
  235.             {
  236.                 c2++;
  237.                 e2->teamchain = e->teamchain;
  238.                 e->teamchain = e2;
  239.                 e2->teammaster = e;
  240.                 e2->flags |= FL_TEAMSLAVE;
  241.  
  242.                 // make sure that targets only point at the master
  243.                 if ( e2->targetname ) {
  244.                     e->targetname = e2->targetname;
  245.                     e2->targetname = NULL;
  246.                 }
  247.             }
  248.         }
  249.     }
  250.  
  251.     G_Printf ("%i teams with %i entities\n", c, c2);
  252. }
  253.  
  254. /*
  255. =================
  256. G_RegisterCvars
  257. =================
  258. */
  259. void G_RegisterCvars( void ) {
  260.     int            i;
  261.     cvarTable_t    *cv;
  262.  
  263.     for ( i = 0, cv = gameCvarTable ; i < gameCvarTableSize ; i++, cv++ ) {
  264.         trap_Cvar_Register( cv->vmCvar, cv->cvarName,
  265.             cv->defaultString, cv->cvarFlags );
  266.         if ( cv->vmCvar )
  267.             cv->modificationCount = cv->vmCvar->modificationCount;
  268.     }
  269.  
  270.     // check some things
  271.  
  272.     if ( g_gametype.integer < 0 || g_gametype.integer >= GT_MAX_GAME_TYPE ) {
  273.         G_Printf( "g_gametype %i is out of range, defaulting to 0\n", g_gametype.integer );
  274.         trap_Cvar_Set( "g_gametype", "0" );
  275.     }
  276.  
  277.     level.warmupModificationCount = g_warmup.modificationCount;
  278. }
  279.  
  280. /*
  281. =================
  282. G_UpdateCvars
  283. =================
  284. */
  285. void G_UpdateCvars( void ) {
  286.     int            i;
  287.     cvarTable_t    *cv;
  288.  
  289.     for ( i = 0, cv = gameCvarTable ; i < gameCvarTableSize ; i++, cv++ ) {
  290.         if ( cv->vmCvar ) {
  291.             trap_Cvar_Update( cv->vmCvar );
  292.  
  293.             if ( cv->modificationCount != cv->vmCvar->modificationCount ) {
  294.                 cv->modificationCount = cv->vmCvar->modificationCount;
  295.  
  296.                 if ( cv->trackChange ) {
  297.                     trap_SendServerCommand( -1, va("print \"Server: %s changed to %s\n\"", 
  298.                         cv->cvarName, cv->vmCvar->string ) );
  299.                 }
  300.             }
  301.         }
  302.     }
  303. }
  304.  
  305.  
  306. /*
  307. ============
  308. G_InitGame
  309.  
  310. ============
  311. */
  312. void G_InitGame( int levelTime, int randomSeed, int restart ) {
  313.     int                    i;
  314.  
  315.     G_Printf ("------- Game Initialization -------\n");
  316.     G_Printf ("gamename: %s\n", GAMEVERSION);
  317.     G_Printf ("gamedate: %s\n", __DATE__);
  318.  
  319.     srand( randomSeed );
  320.  
  321.     G_RegisterCvars();
  322.  
  323.     G_ProcessIPBans();
  324.  
  325.     G_InitMemory();
  326.  
  327.     // set some level globals
  328.     memset( &level, 0, sizeof( level ) );
  329.     level.time = levelTime;
  330.     level.startTime = levelTime;
  331.  
  332.     level.snd_fry = G_SoundIndex("sound/player/fry.wav");    // FIXME standing in lava / slime
  333.  
  334.     if ( g_gametype.integer != GT_SINGLE_PLAYER && g_log.string[0] ) {
  335.         if ( g_logSync.integer ) {
  336.             trap_FS_FOpenFile( g_log.string, &level.logFile, FS_APPEND_SYNC );
  337.         } else {
  338.             trap_FS_FOpenFile( g_log.string, &level.logFile, FS_APPEND );
  339.         }
  340.         if ( !level.logFile ) {
  341.             G_Printf( "WARNING: Couldn't open logfile: %s\n", g_log.string );
  342.         } else {
  343.             char    serverinfo[MAX_INFO_STRING];
  344.  
  345.             trap_GetServerinfo( serverinfo, sizeof( serverinfo ) );
  346.  
  347.             G_LogPrintf("------------------------------------------------------------\n" );
  348.             G_LogPrintf("InitGame: %s\n", serverinfo );
  349.         }
  350.     } else {
  351.         G_Printf( "Not logging to disk.\n" );
  352.     }
  353.  
  354.     G_InitWorldSession();
  355.  
  356.     // initialize all entities for this game
  357.     memset( g_entities, 0, MAX_GENTITIES * sizeof(g_entities[0]) );
  358.     level.gentities = g_entities;
  359.  
  360.     // initialize all clients for this game
  361.     level.maxclients = g_maxclients.integer;
  362.     memset( g_clients, 0, MAX_CLIENTS * sizeof(g_clients[0]) );
  363.     level.clients = g_clients;
  364.  
  365.     // set client fields on player ents
  366.     for ( i=0 ; i<level.maxclients ; i++ ) {
  367.         g_entities[i].client = level.clients + i;
  368.     }
  369.  
  370.     // always leave room for the max number of clients,
  371.     // even if they aren't all used, so numbers inside that
  372.     // range are NEVER anything but clients
  373.     level.num_entities = MAX_CLIENTS;
  374.  
  375.     // let the server system know where the entites are
  376.     trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ), 
  377.         &level.clients[0].ps, sizeof( level.clients[0] ) );
  378.  
  379.     // reserve some spots for dead player bodies
  380.     InitBodyQue();
  381.  
  382.     ClearRegisteredItems();
  383.  
  384.     // parse the key/value pairs and spawn gentities
  385.     G_SpawnEntitiesFromString();
  386.  
  387.     // general initialization
  388.     G_FindTeams();
  389.  
  390.     // make sure we have flags for CTF, etc
  391.     G_CheckTeamItems();
  392.  
  393.     SaveRegisteredItems();
  394.  
  395.     G_Printf ("-----------------------------------\n");
  396.  
  397.     if( g_gametype.integer == GT_SINGLE_PLAYER || trap_Cvar_VariableIntegerValue( "com_buildScript" ) ) {
  398.         G_ModelIndex( SP_PODIUM_MODEL );
  399.         G_SoundIndex( "sound/player/gurp1.wav" );
  400.         G_SoundIndex( "sound/player/gurp2.wav" );
  401.     }
  402.  
  403.     if ( trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
  404.         BotAISetup( restart );
  405.         BotAILoadMap( restart );
  406.         G_InitBots( restart );
  407.     }
  408. }
  409.  
  410.  
  411.  
  412. /*
  413. =================
  414. G_ShutdownGame
  415. =================
  416. */
  417. void G_ShutdownGame( int restart ) {
  418.     G_Printf ("==== ShutdownGame ====\n");
  419.  
  420.     if ( level.logFile ) {
  421.         G_LogPrintf("ShutdownGame:\n" );
  422.         G_LogPrintf("------------------------------------------------------------\n" );
  423.         trap_FS_FCloseFile( level.logFile );
  424.     }
  425.  
  426.     // write all the client session data so we can get it back
  427.     G_WriteSessionData();
  428.  
  429.     if ( trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
  430.         BotAIShutdown( restart );
  431.     }
  432. }
  433.  
  434.  
  435.  
  436. //===================================================================
  437.  
  438. #ifndef GAME_HARD_LINKED
  439. // this is only here so the functions in q_shared.c and bg_*.c can link
  440.  
  441. void QDECL Com_Error ( int level, const char *error, ... ) {
  442.     va_list        argptr;
  443.     char        text[1024];
  444.  
  445.     va_start (argptr, error);
  446.     vsprintf (text, error, argptr);
  447.     va_end (argptr);
  448.  
  449.     G_Error( "%s", text);
  450. }
  451.  
  452. void QDECL Com_Printf( const char *msg, ... ) {
  453.     va_list        argptr;
  454.     char        text[1024];
  455.  
  456.     va_start (argptr, msg);
  457.     vsprintf (text, msg, argptr);
  458.     va_end (argptr);
  459.  
  460.     G_Printf ("%s", text);
  461. }
  462.  
  463. #endif
  464.  
  465. /*
  466. ========================================================================
  467.  
  468. PLAYER COUNTING / SCORE SORTING
  469.  
  470. ========================================================================
  471. */
  472.  
  473. /*
  474. =============
  475. AddTournamentPlayer
  476.  
  477. If there are less than two tournament players, put a
  478. spectator in the game and restart
  479. =============
  480. */
  481. void AddTournamentPlayer( void ) {
  482.     int            i;
  483.     gclient_t    *client;
  484.     gclient_t    *nextInLine;
  485.  
  486.     if ( level.numPlayingClients >= 2 ) {
  487.         return;
  488.     }
  489.  
  490.     // never change during intermission
  491.     if ( level.intermissiontime ) {
  492.         return;
  493.     }
  494.  
  495.     nextInLine = NULL;
  496.  
  497.     for ( i = 0 ; i < level.maxclients ; i++ ) {
  498.         client = &level.clients[i];
  499.         if ( client->pers.connected != CON_CONNECTED ) {
  500.             continue;
  501.         }
  502.         if ( client->sess.sessionTeam != TEAM_SPECTATOR ) {
  503.             continue;
  504.         }
  505.         // never select the dedicated follow or scoreboard clients
  506.         if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD || 
  507.             client->sess.spectatorClient < 0  ) {
  508.             continue;
  509.         }
  510.  
  511.         if ( !nextInLine || client->sess.spectatorTime < nextInLine->sess.spectatorTime ) {
  512.             nextInLine = client;
  513.         }
  514.     }
  515.  
  516.     if ( !nextInLine ) {
  517.         return;
  518.     }
  519.  
  520.     level.warmupTime = -1;
  521.  
  522.     // set them to free-for-all team
  523.     SetTeam( &g_entities[ nextInLine - level.clients ], "f" );
  524. }
  525.  
  526.  
  527. /*
  528. =======================
  529. RemoveTournamentLoser
  530.  
  531. Make the loser a spectator at the back of the line
  532. =======================
  533. */
  534. void RemoveTournamentLoser( void ) {
  535.     int            clientNum;
  536.  
  537.     if ( level.numPlayingClients != 2 ) {
  538.         return;
  539.     }
  540.  
  541.     clientNum = level.sortedClients[1];
  542.  
  543.     if ( level.clients[ clientNum ].pers.connected != CON_CONNECTED ) {
  544.         return;
  545.     }
  546.  
  547.     // make them a spectator
  548.     SetTeam( &g_entities[ clientNum ], "s" );
  549. }
  550.  
  551.  
  552. /*
  553. =======================
  554. AdjustTournamentScores
  555.  
  556. =======================
  557. */
  558. void AdjustTournamentScores( void ) {
  559.     int            clientNum;
  560.  
  561.     clientNum = level.sortedClients[0];
  562.     if ( level.clients[ clientNum ].pers.connected == CON_CONNECTED ) {
  563.         level.clients[ clientNum ].sess.wins++;
  564.         ClientUserinfoChanged( clientNum );
  565.     }
  566.  
  567.     clientNum = level.sortedClients[1];
  568.     if ( level.clients[ clientNum ].pers.connected == CON_CONNECTED ) {
  569.         level.clients[ clientNum ].sess.losses++;
  570.         ClientUserinfoChanged( clientNum );
  571.     }
  572.  
  573. }
  574.  
  575.  
  576.  
  577. /*
  578. =============
  579. SortRanks
  580.  
  581. =============
  582. */
  583. int QDECL SortRanks( const void *a, const void *b ) {
  584.     gclient_t    *ca, *cb;
  585.  
  586.     ca = &level.clients[*(int *)a];
  587.     cb = &level.clients[*(int *)b];
  588.  
  589.     // sort special clients last
  590.     if ( ca->sess.spectatorState == SPECTATOR_SCOREBOARD || ca->sess.spectatorClient < 0 ) {
  591.         return 1;
  592.     }
  593.     if ( cb->sess.spectatorState == SPECTATOR_SCOREBOARD || cb->sess.spectatorClient < 0  ) {
  594.         return -1;
  595.     }
  596.  
  597.     // then connecting clients
  598.     if ( ca->pers.connected == CON_CONNECTING ) {
  599.         return 1;
  600.     }
  601.     if ( cb->pers.connected == CON_CONNECTING ) {
  602.         return -1;
  603.     }
  604.  
  605.  
  606.     // then spectators
  607.     if ( ca->sess.sessionTeam == TEAM_SPECTATOR && cb->sess.sessionTeam == TEAM_SPECTATOR ) {
  608.         if ( ca->sess.spectatorTime < cb->sess.spectatorTime ) {
  609.             return -1;
  610.         }
  611.         if ( ca->sess.spectatorTime > cb->sess.spectatorTime ) {
  612.             return 1;
  613.         }
  614.         return 0;
  615.     }
  616.     if ( ca->sess.sessionTeam == TEAM_SPECTATOR ) {
  617.         return 1;
  618.     }
  619.     if ( cb->sess.sessionTeam == TEAM_SPECTATOR ) {
  620.         return -1;
  621.     }
  622.  
  623.     // then sort by score
  624.     if ( ca->ps.persistant[PERS_SCORE]
  625.         > cb->ps.persistant[PERS_SCORE] ) {
  626.         return -1;
  627.     }
  628.     if ( ca->ps.persistant[PERS_SCORE]
  629.         < cb->ps.persistant[PERS_SCORE] ) {
  630.         return 1;
  631.     }
  632.     return 0;
  633. }
  634.  
  635. /*
  636. ============
  637. CalculateRanks
  638.  
  639. Recalculates the score ranks of all players
  640. This will be called on every client connect, begin, disconnect, death,
  641. and team change.
  642. ============
  643. */
  644. void CalculateRanks( void ) {
  645.     int        i;
  646.     int        rank;
  647.     int        score;
  648.     int        newScore;
  649.     gclient_t    *cl;
  650.  
  651.     level.follow1 = -1;
  652.     level.follow2 = -1;
  653.     level.numConnectedClients = 0;
  654.     level.numNonSpectatorClients = 0;
  655.     level.numPlayingClients = 0;
  656.     level.numVotingClients = 0;        // don't count bots
  657.     for ( i = 0 ; i < level.maxclients ; i++ ) {
  658.         if ( level.clients[i].pers.connected != CON_DISCONNECTED ) {
  659.             level.sortedClients[level.numConnectedClients] = i;
  660.             level.numConnectedClients++;
  661.  
  662.             if ( level.clients[i].sess.sessionTeam != TEAM_SPECTATOR ) {
  663.                 level.numNonSpectatorClients++;
  664.             
  665.                 // decide if this should be auto-followed
  666.                 if ( level.clients[i].pers.connected == CON_CONNECTED ) {
  667.                     level.numPlayingClients++;
  668.                     if ( !(g_entities[i].r.svFlags & SVF_BOT) ) {
  669.                         level.numVotingClients++;
  670.                     }
  671.                     if ( level.follow1 == -1 ) {
  672.                         level.follow1 = i;
  673.                     } else if ( level.follow2 == -1 ) {
  674.                         level.follow2 = i;
  675.                     }
  676.                 }
  677.             }
  678.         }
  679.     }
  680.  
  681.     qsort( level.sortedClients, level.numConnectedClients, 
  682.         sizeof(level.sortedClients[0]), SortRanks );
  683.  
  684.     // set the rank value for all clients that are connected and not spectators
  685.     if ( g_gametype.integer >= GT_TEAM ) {
  686.         // in team games, rank is just the order of the teams, 0=red, 1=blue, 2=tied
  687.         for ( i = 0;  i < level.numConnectedClients; i++ ) {
  688.             cl = &level.clients[ level.sortedClients[i] ];
  689.             if ( level.teamScores[TEAM_RED] == level.teamScores[TEAM_BLUE] ) {
  690.                 cl->ps.persistant[PERS_RANK] = 2;
  691.             } else if ( level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE] ) {
  692.                 cl->ps.persistant[PERS_RANK] = 0;
  693.             } else {
  694.                 cl->ps.persistant[PERS_RANK] = 1;
  695.             }
  696.         }
  697.     } else {    
  698.         rank = -1;
  699.         score = 0;
  700.         for ( i = 0;  i < level.numPlayingClients; i++ ) {
  701.             cl = &level.clients[ level.sortedClients[i] ];
  702.             newScore = cl->ps.persistant[PERS_SCORE];
  703.             if ( i == 0 || newScore != score ) {
  704.                 rank = i;
  705.                 // assume we aren't tied until the next client is checked
  706.                 level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank;
  707.             } else {
  708.                 // we are tied with the previous client
  709.                 level.clients[ level.sortedClients[i-1] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG;
  710.                 level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG;
  711.             }
  712.             score = newScore;
  713.             if ( g_gametype.integer == GT_SINGLE_PLAYER && level.numPlayingClients == 1 ) {
  714.                 level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG;
  715.             }
  716.         }
  717.     }
  718.  
  719.     // set the CS_SCORES1/2 configstrings, which will be visible to everyone
  720.     if ( g_gametype.integer >= GT_TEAM ) {
  721.         trap_SetConfigstring( CS_SCORES1, va("%i", level.teamScores[TEAM_RED] ) );
  722.         trap_SetConfigstring( CS_SCORES2, va("%i", level.teamScores[TEAM_BLUE] ) );
  723.     } else {
  724.         if ( level.numConnectedClients == 0 ) {
  725.             trap_SetConfigstring( CS_SCORES1, va("%i", SCORE_NOT_PRESENT) );
  726.             trap_SetConfigstring( CS_SCORES2, va("%i", SCORE_NOT_PRESENT) );
  727.         } else if ( level.numConnectedClients == 1 ) {
  728.             trap_SetConfigstring( CS_SCORES1, va("%i", level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE] ) );
  729.             trap_SetConfigstring( CS_SCORES2, va("%i", SCORE_NOT_PRESENT) );
  730.         } else {
  731.             trap_SetConfigstring( CS_SCORES1, va("%i", level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE] ) );
  732.             trap_SetConfigstring( CS_SCORES2, va("%i", level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE] ) );
  733.         }
  734.     }
  735.  
  736.     // see if it is time to end the level
  737.     CheckExitRules();
  738.  
  739.     // if we are at the intermission, send the new info to everyone
  740.     if ( level.intermissiontime ) {
  741.         SendScoreboardMessageToAllClients();
  742.     }
  743. }
  744.  
  745.  
  746. /*
  747. ========================================================================
  748.  
  749. MAP CHANGING
  750.  
  751. ========================================================================
  752. */
  753.  
  754. /*
  755. ========================
  756. SendScoreboardMessageToAllClients
  757.  
  758. Do this at BeginIntermission time and whenever ranks are recalculated
  759. due to enters/exits/forced team changes
  760. ========================
  761. */
  762. void SendScoreboardMessageToAllClients( void ) {
  763.     int        i;
  764.  
  765.     for ( i = 0 ; i < level.maxclients ; i++ ) {
  766.         if ( level.clients[ i ].pers.connected == CON_CONNECTED ) {
  767.             DeathmatchScoreboardMessage( g_entities + i );
  768.         }
  769.     }
  770. }
  771.  
  772. /*
  773. ========================
  774. MoveClientToIntermission
  775.  
  776. When the intermission starts, this will be called for all players.
  777. If a new client connects, this will be called after the spawn function.
  778. ========================
  779. */
  780. void MoveClientToIntermission( gentity_t *ent ) {
  781.     // take out of follow mode if needed
  782.     if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
  783.         StopFollowing( ent );
  784.     }
  785.  
  786.  
  787.     // move to the spot
  788.     VectorCopy( level.intermission_origin, ent->s.origin );
  789.     VectorCopy( level.intermission_origin, ent->client->ps.origin );
  790.     VectorCopy (level.intermission_angle, ent->client->ps.viewangles);
  791.     ent->client->ps.pm_type = PM_INTERMISSION;
  792.  
  793.     // clean up powerup info
  794.     memset( ent->client->ps.powerups, 0, sizeof(ent->client->ps.powerups) );
  795.  
  796.     ent->client->ps.eFlags = 0;
  797.     ent->s.eFlags = 0;
  798.     ent->s.eType = ET_GENERAL;
  799.     ent->s.modelindex = 0;
  800.     ent->s.loopSound = 0;
  801.     ent->s.event = 0;
  802.     ent->r.contents = 0;
  803. }
  804.  
  805. /*
  806. ==================
  807. FindIntermissionPoint
  808.  
  809. This is also used for spectator spawns
  810. ==================
  811. */
  812. void FindIntermissionPoint( void ) {
  813.     gentity_t    *ent, *target;
  814.     vec3_t        dir;
  815.  
  816.     // find the intermission spot
  817.     ent = G_Find (NULL, FOFS(classname), "info_player_intermission");
  818.     if ( !ent ) {    // the map creator forgot to put in an intermission point...
  819.         SelectSpawnPoint ( vec3_origin, level.intermission_origin, level.intermission_angle );
  820.     } else {
  821.         VectorCopy (ent->s.origin, level.intermission_origin);
  822.         VectorCopy (ent->s.angles, level.intermission_angle);
  823.         // if it has a target, look towards it
  824.         if ( ent->target ) {
  825.             target = G_PickTarget( ent->target );
  826.             if ( target ) {
  827.                 VectorSubtract( target->s.origin, level.intermission_origin, dir );
  828.                 vectoangles( dir, level.intermission_angle );
  829.             }
  830.         }
  831.     }
  832.  
  833. }
  834.  
  835. /*
  836. ==================
  837. BeginIntermission
  838. ==================
  839. */
  840. void BeginIntermission( void ) {
  841.     int            i;
  842.     gentity_t    *client;
  843.  
  844.     if ( level.intermissiontime ) {
  845.         return;        // already active
  846.     }
  847.  
  848.     // if in tournement mode, change the wins / losses
  849.     if ( g_gametype.integer == GT_TOURNAMENT ) {
  850.         AdjustTournamentScores();
  851.     }
  852.  
  853.     level.intermissiontime = level.time;
  854.     FindIntermissionPoint();
  855.  
  856.     // if single player game
  857.     if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
  858.         UpdateTournamentInfo();
  859.         SpawnModelsOnVictoryPads();
  860.     }
  861.  
  862.     // move all clients to the intermission point
  863.     for (i=0 ; i< level.maxclients ; i++) {
  864.         client = g_entities + i;
  865.         if (!client->inuse)
  866.             continue;
  867.         // respawn if dead
  868.         if (client->health <= 0) {
  869.             respawn(client);
  870.         }
  871.         MoveClientToIntermission( client );
  872.     }
  873.  
  874.     // send the current scoring to all clients
  875.     SendScoreboardMessageToAllClients();
  876. }
  877.  
  878.  
  879. /*
  880. =============
  881. ExitLevel
  882.  
  883. When the intermission has been exited, the server is either killed
  884. or moved to a new level based on the "nextmap" cvar 
  885.  
  886. =============
  887. */
  888. void ExitLevel (void) {
  889.     int        i;
  890.  
  891.     //bot interbreeding
  892.     BotInterbreedEndMatch();
  893.  
  894.     // if we are running a tournement map, kick the loser to spectator status,
  895.     // which will automatically grab the next spectator and restart
  896.     if ( g_gametype.integer == GT_TOURNAMENT ) {
  897.         if ( !level.restarted ) {
  898.             RemoveTournamentLoser();
  899.             trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
  900.             level.restarted = qtrue;
  901.             level.changemap = NULL;
  902.             level.intermissiontime = 0;
  903.         }
  904.         return;    
  905.     }
  906.  
  907.  
  908.     trap_SendConsoleCommand( EXEC_APPEND, "vstr nextmap\n" );
  909.     level.changemap = NULL;
  910.     level.intermissiontime = 0;
  911.  
  912.     // we need to do this here before chaning to CON_CONNECTING
  913.     G_WriteSessionData();
  914.  
  915.     // change all client states to connecting, so the early players into the
  916.     // next level will know the others aren't done reconnecting
  917.     for (i=0 ; i< g_maxclients.integer ; i++) {
  918.         if ( level.clients[i].pers.connected == CON_CONNECTED ) {
  919.             level.clients[i].pers.connected = CON_CONNECTING;
  920.         }
  921.     }
  922.  
  923. }
  924.  
  925. /*
  926. =================
  927. G_LogPrintf
  928.  
  929. Print to the logfile with a time stamp if it is open
  930. =================
  931. */
  932. void QDECL G_LogPrintf( const char *fmt, ... ) {
  933.     va_list        argptr;
  934.     char        string[1024];
  935.     int            min, tens, sec;
  936.  
  937.     sec = level.time / 1000;
  938.  
  939.     min = sec / 60;
  940.     sec -= min * 60;
  941.     tens = sec / 10;
  942.     sec -= tens * 10;
  943.  
  944.     Com_sprintf( string, sizeof(string), "%3i:%i%i ", min, tens, sec );
  945.  
  946.     va_start( argptr, fmt );
  947.     vsprintf( string +7 , fmt,argptr );
  948.     va_end( argptr );
  949.  
  950.     if ( g_dedicated.integer ) {
  951.         G_Printf( "%s", string + 7 );
  952.     }
  953.  
  954.     if ( !level.logFile ) {
  955.         return;
  956.     }
  957.  
  958.     trap_FS_Write( string, strlen( string ), level.logFile );
  959. }
  960.  
  961. /*
  962. ================
  963. LogExit
  964.  
  965. Append information about this game to the log file
  966. ================
  967. */
  968. void LogExit( const char *string ) {
  969.     int                i, numSorted;
  970.     gclient_t        *cl;
  971.  
  972.     G_LogPrintf( "Exit: %s\n", string );
  973.  
  974.     level.intermissionQueued = level.time;
  975.  
  976.     // this will keep the clients from playing any voice sounds
  977.     // that will get cut off when the queued intermission starts
  978.     trap_SetConfigstring( CS_INTERMISSION, "1" );
  979.  
  980.     // don't send more than 32 scores (FIXME?)
  981.     numSorted = level.numConnectedClients;
  982.     if ( numSorted > 32 ) {
  983.         numSorted = 32;
  984.     }
  985.  
  986.     if ( g_gametype.integer >= GT_TEAM ) {
  987.         G_LogPrintf( "red:%i  blue:%i\n",
  988.             level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE] );
  989.     }
  990.  
  991.     for (i=0 ; i < numSorted ; i++) {
  992.         int        ping;
  993.  
  994.         cl = &level.clients[level.sortedClients[i]];
  995.  
  996.         if ( cl->sess.sessionTeam == TEAM_SPECTATOR ) {
  997.             continue;
  998.         }
  999.         if ( cl->pers.connected == CON_CONNECTING ) {
  1000.             continue;
  1001.         }
  1002.  
  1003.         ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
  1004.  
  1005.         G_LogPrintf( "score: %i  ping: %i  client: %i %s\n", 
  1006.             cl->ps.persistant[PERS_SCORE], ping, level.sortedClients[i],
  1007.             cl->pers.netname );
  1008.     }
  1009. }
  1010.  
  1011.  
  1012. /*
  1013. =================
  1014. CheckIntermissionExit
  1015.  
  1016. The level will stay at the intermission for a minimum of 5 seconds
  1017. If all players wish to continue, the level will then exit.
  1018. If one or more players have not acknowledged the continue, the game will
  1019. wait 10 seconds before going on.
  1020. =================
  1021. */
  1022. void CheckIntermissionExit( void ) {
  1023.     int            ready, notReady;
  1024.     int            i;
  1025.     gclient_t    *cl;
  1026.     int            readyMask;
  1027.  
  1028.     if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
  1029.         return;
  1030.     }
  1031.  
  1032.     // see which players are ready
  1033.     ready = 0;
  1034.     notReady = 0;
  1035.     readyMask = 0;
  1036.     for (i=0 ; i< g_maxclients.integer ; i++) {
  1037.         cl = level.clients + i;
  1038.         if ( cl->pers.connected != CON_CONNECTED ) {
  1039.             continue;
  1040.         }
  1041.         if ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) {
  1042.             continue;
  1043.         }
  1044.  
  1045.         if ( cl->readyToExit ) {
  1046.             ready++;
  1047.             if ( i < 16 ) {
  1048.                 readyMask |= 1 << i;
  1049.             }
  1050.         } else {
  1051.             notReady++;
  1052.         }
  1053.     }
  1054.  
  1055.     // copy the readyMask to each player's stats so
  1056.     // it can be displayed on the scoreboard
  1057.     for (i=0 ; i< g_maxclients.integer ; i++) {
  1058.         cl = level.clients + i;
  1059.         if ( cl->pers.connected != CON_CONNECTED ) {
  1060.             continue;
  1061.         }
  1062.         cl->ps.stats[STAT_CLIENTS_READY] = readyMask;
  1063.     }
  1064.  
  1065.     // never exit in less than five seconds
  1066.     if ( level.time < level.intermissiontime + 5000 ) {
  1067.         return;
  1068.     }
  1069.  
  1070.     // if nobody wants to go, clear timer
  1071.     if ( !ready ) {
  1072.         level.readyToExit = qfalse;
  1073.         return;
  1074.     }
  1075.  
  1076.     // if everyone wants to go, go now
  1077.     if ( !notReady ) {
  1078.         ExitLevel();
  1079.         return;
  1080.     }
  1081.  
  1082.     // the first person to ready starts the ten second timeout
  1083.     if ( !level.readyToExit ) {
  1084.         level.readyToExit = qtrue;
  1085.         level.exitTime = level.time;
  1086.     }
  1087.  
  1088.     // if we have waited ten seconds since at least one player
  1089.     // wanted to exit, go ahead
  1090.     if ( level.time < level.exitTime + 10000 ) {
  1091.         return;
  1092.     }
  1093.  
  1094.     ExitLevel();
  1095. }
  1096.  
  1097. /*
  1098. =============
  1099. ScoreIsTied
  1100. =============
  1101. */
  1102. qboolean ScoreIsTied( void ) {
  1103.     int        a, b;
  1104.  
  1105.     if ( level.numPlayingClients < 2 ) {
  1106.         return qfalse;
  1107.     }
  1108.     
  1109.     if ( g_gametype.integer >= GT_TEAM ) {
  1110.         return level.teamScores[TEAM_RED] == level.teamScores[TEAM_BLUE];
  1111.     }
  1112.  
  1113.     a = level.clients[level.sortedClients[0]].ps.persistant[PERS_SCORE];
  1114.     b = level.clients[level.sortedClients[1]].ps.persistant[PERS_SCORE];
  1115.  
  1116.     return a == b;
  1117. }
  1118.  
  1119. /*
  1120. =================
  1121. CheckExitRules
  1122.  
  1123. There will be a delay between the time the exit is qualified for
  1124. and the time everyone is moved to the intermission spot, so you
  1125. can see the last frag.
  1126. =================
  1127. */
  1128. void CheckExitRules( void ) {
  1129.     int            i;
  1130.     gclient_t    *cl;
  1131.  
  1132.     // if at the intermission, wait for all non-bots to
  1133.     // signal ready, then go to next level
  1134.     if ( level.intermissiontime ) {
  1135.         CheckIntermissionExit ();
  1136.         return;
  1137.     }
  1138.  
  1139.     if ( level.intermissionQueued ) {
  1140.         if ( level.time - level.intermissionQueued >= INTERMISSION_DELAY_TIME ) {
  1141.             level.intermissionQueued = 0;
  1142.             BeginIntermission();
  1143.         }
  1144.         return;
  1145.     }
  1146.  
  1147.     if ( g_timelimit.integer && !level.warmupTime ) {
  1148.         if ( level.time - level.startTime >= g_timelimit.integer*60000 ) {
  1149.             // check for sudden death 
  1150.             if ( g_gametype.integer != GT_CTF && ScoreIsTied() ) {
  1151.                 // score is tied, so don't end the game
  1152.                 return;
  1153.             }
  1154.             trap_SendServerCommand( -1, "print \"Timelimit hit.\n\"");
  1155.             LogExit( "Timelimit hit." );
  1156.             return;
  1157.         }
  1158.     }
  1159.  
  1160.     if ( level.numPlayingClients < 2 ) {
  1161.         return;
  1162.     }
  1163.  
  1164.     if ( g_gametype.integer != GT_CTF && g_fraglimit.integer ) {
  1165.         if ( level.teamScores[TEAM_RED] >= g_fraglimit.integer ) {
  1166.             trap_SendServerCommand( -1, "print \"Red hit the fraglimit.\n\"" );
  1167.             LogExit( "Fraglimit hit." );
  1168.             return;
  1169.         }
  1170.  
  1171.         if ( level.teamScores[TEAM_BLUE] >= g_fraglimit.integer ) {
  1172.             trap_SendServerCommand( -1, "print \"Blue hit the fraglimit.\n\"" );
  1173.             LogExit( "Fraglimit hit." );
  1174.             return;
  1175.         }
  1176.  
  1177.         for ( i=0 ; i< g_maxclients.integer ; i++ ) {
  1178.             cl = level.clients + i;
  1179.             if ( cl->pers.connected != CON_CONNECTED ) {
  1180.                 continue;
  1181.             }
  1182.             if ( cl->sess.sessionTeam != TEAM_FREE ) {
  1183.                 continue;
  1184.             }
  1185.  
  1186.             if ( cl->ps.persistant[PERS_SCORE] >= g_fraglimit.integer ) {
  1187.                 LogExit( "Fraglimit hit." );
  1188.                 trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " hit the fraglimit.\n\"",
  1189.                     cl->pers.netname ) );
  1190.                 return;
  1191.             }
  1192.         }
  1193.     }
  1194.  
  1195.     if ( g_gametype.integer == GT_CTF && g_capturelimit.integer ) {
  1196.  
  1197.         if ( level.teamScores[TEAM_RED] >= g_capturelimit.integer ) {
  1198.             trap_SendServerCommand( -1, "print \"Red hit the capturelimit.\n\"" );
  1199.             LogExit( "Capturelimit hit." );
  1200.             return;
  1201.         }
  1202.  
  1203.         if ( level.teamScores[TEAM_BLUE] >= g_capturelimit.integer ) {
  1204.             trap_SendServerCommand( -1, "print \"Blue hit the capturelimit.\n\"" );
  1205.             LogExit( "Capturelimit hit." );
  1206.             return;
  1207.         }
  1208.     }
  1209. }
  1210.  
  1211.  
  1212.  
  1213. /*
  1214. ========================================================================
  1215.  
  1216. FUNCTIONS CALLED EVERY FRAME
  1217.  
  1218. ========================================================================
  1219. */
  1220.  
  1221.  
  1222. /*
  1223. =============
  1224. CheckTournement
  1225.  
  1226. Once a frame, check for changes in tournement player state
  1227. =============
  1228. */
  1229. void CheckTournement( void ) {
  1230.     if ( level.numPlayingClients == 0 ) {
  1231.         return;
  1232.     }
  1233.  
  1234.     if ( g_gametype.integer == GT_TOURNAMENT ) {
  1235.  
  1236.         // pull in a spectator if needed
  1237.         if ( level.numPlayingClients < 2 ) {
  1238.             AddTournamentPlayer();
  1239.         }
  1240.  
  1241.         // if we don't have two players, go back to "waiting for players"
  1242.         if ( level.numPlayingClients != 2 ) {
  1243.             if ( level.warmupTime != -1 ) {
  1244.                 level.warmupTime = -1;
  1245.                 trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
  1246.                 G_LogPrintf( "Warmup:\n" );
  1247.             }
  1248.             return;
  1249.         }
  1250.  
  1251.         if ( level.warmupTime == 0 ) {
  1252.             return;
  1253.         }
  1254.  
  1255.         // if the warmup is changed at the console, restart it
  1256.         if ( g_warmup.modificationCount != level.warmupModificationCount ) {
  1257.             level.warmupModificationCount = g_warmup.modificationCount;
  1258.             level.warmupTime = -1;
  1259.         }
  1260.  
  1261.         // if all players have arrived, start the countdown
  1262.         if ( level.warmupTime < 0 ) {
  1263.             if ( level.numPlayingClients == 2 ) {
  1264.                 // fudge by -1 to account for extra delays
  1265.                 level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
  1266.                 trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
  1267.             }
  1268.             return;
  1269.         }
  1270.  
  1271.         // if the warmup time has counted down, restart
  1272.         if ( level.time > level.warmupTime ) {
  1273.             level.warmupTime += 10000;
  1274.             trap_Cvar_Set( "g_restarted", "1" );
  1275.             trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
  1276.             level.restarted = qtrue;
  1277.             return;
  1278.         }
  1279.     } else if ( g_gametype.integer != GT_SINGLE_PLAYER && g_doWarmup.integer ) {
  1280.         int        counts[TEAM_NUM_TEAMS];
  1281.         qboolean    notEnough = qfalse;
  1282.  
  1283.         if ( g_gametype.integer > GT_TEAM ) {
  1284.             counts[TEAM_BLUE] = TeamCount( -1, TEAM_BLUE );
  1285.             counts[TEAM_RED] = TeamCount( -1, TEAM_RED );
  1286.  
  1287.             if (counts[TEAM_RED] < 1 || counts[TEAM_BLUE] < 1) {
  1288.                 notEnough = qtrue;
  1289.             }
  1290.         } else if ( level.numPlayingClients < 2 ) {
  1291.             notEnough = qtrue;
  1292.         }
  1293.  
  1294.         if ( notEnough ) {
  1295.             if ( level.warmupTime != -1 ) {
  1296.                 level.warmupTime = -1;
  1297.                 trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
  1298.                 G_LogPrintf( "Warmup:\n" );
  1299.             }
  1300.             return; // still waiting for team members
  1301.         }
  1302.  
  1303.         if ( level.warmupTime == 0 ) {
  1304.             return;
  1305.         }
  1306.  
  1307.         // if the warmup is changed at the console, restart it
  1308.         if ( g_warmup.modificationCount != level.warmupModificationCount ) {
  1309.             level.warmupModificationCount = g_warmup.modificationCount;
  1310.             level.warmupTime = -1;
  1311.         }
  1312.  
  1313.         // if all players have arrived, start the countdown
  1314.         if ( level.warmupTime < 0 ) {
  1315.             // fudge by -1 to account for extra delays
  1316.             level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
  1317.             trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
  1318.             return;
  1319.         }
  1320.  
  1321.         // if the warmup time has counted down, restart
  1322.         if ( level.time > level.warmupTime ) {
  1323.             level.warmupTime += 10000;
  1324.             trap_Cvar_Set( "g_restarted", "1" );
  1325.             trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
  1326.             level.restarted = qtrue;
  1327.             return;
  1328.         }
  1329.     }
  1330. }
  1331.  
  1332.  
  1333. /*
  1334. ==================
  1335. CheckVote
  1336. ==================
  1337. */
  1338. void CheckVote( void ) {
  1339.     if ( !level.voteTime ) {
  1340.         return;
  1341.     }
  1342.     if ( level.time - level.voteTime >= VOTE_TIME ) {
  1343.         trap_SendServerCommand( -1, "print \"Vote failed.\n\"" );
  1344.     } else {
  1345.         if ( level.voteYes > level.numVotingClients/2 ) {
  1346.             // execute the command, then remove the vote
  1347.             trap_SendServerCommand( -1, "print \"Vote passed.\n\"" );
  1348.             trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.voteString ) );
  1349.         } else if ( level.voteNo >= level.numVotingClients/2 ) {
  1350.             // same behavior as a timeout
  1351.             trap_SendServerCommand( -1, "print \"Vote failed.\n\"" );
  1352.         } else {
  1353.             // still waiting for a majority
  1354.             return;
  1355.         }
  1356.     }
  1357.     level.voteTime = 0;
  1358.     trap_SetConfigstring( CS_VOTE_TIME, "" );
  1359.  
  1360. }
  1361.  
  1362.  
  1363. /*
  1364. ==================
  1365. CheckCvars
  1366. ==================
  1367. */
  1368. void CheckCvars( void ) {
  1369.     static int lastMod = -1;
  1370.  
  1371.     if ( g_password.modificationCount != lastMod ) {
  1372.         lastMod = g_password.modificationCount;
  1373.         if ( *g_password.string && Q_stricmp( g_password.string, "none" ) ) {
  1374.             trap_Cvar_Set( "g_needpass", "1" );
  1375.         } else {
  1376.             trap_Cvar_Set( "g_needpass", "0" );
  1377.         }
  1378.     }
  1379. }
  1380.  
  1381. /*
  1382. =============
  1383. G_RunThink
  1384.  
  1385. Runs thinking code for this frame if necessary
  1386. =============
  1387. */
  1388. void G_RunThink (gentity_t *ent) {
  1389.     float    thinktime;
  1390.  
  1391.     thinktime = ent->nextthink;
  1392.     if (thinktime <= 0) {
  1393.         return;
  1394.     }
  1395.     if (thinktime > level.time) {
  1396.         return;
  1397.     }
  1398.     
  1399.     ent->nextthink = 0;
  1400.     if (!ent->think) {
  1401.         G_Error ( "NULL ent->think");
  1402.     }
  1403.     ent->think (ent);
  1404. }
  1405.  
  1406. /*
  1407. ================
  1408. G_RunFrame
  1409.  
  1410. Advances the non-player objects in the world
  1411. ================
  1412. */
  1413. void G_RunFrame( int levelTime ) {
  1414.     int            i;
  1415.     gentity_t    *ent;
  1416.     int            msec;
  1417. int start, end;
  1418.  
  1419.     // if we are waiting for the level to restart, do nothing
  1420.     if ( level.restarted ) {
  1421.         return;
  1422.     }
  1423.  
  1424.     level.framenum++;
  1425.     level.previousTime = level.time;
  1426.     level.time = levelTime;
  1427.     msec = level.time - level.previousTime;
  1428.  
  1429.     // get any cvar changes
  1430.     G_UpdateCvars();
  1431.  
  1432.     //
  1433.     // go through all allocated objects
  1434.     //
  1435. start = trap_Milliseconds();
  1436.     ent = &g_entities[0];
  1437.     for (i=0 ; i<level.num_entities ; i++, ent++) {
  1438.         if ( !ent->inuse ) {
  1439.             continue;
  1440.         }
  1441.  
  1442.         // clear events that are too old
  1443.         if ( level.time - ent->eventTime > EVENT_VALID_MSEC ) {
  1444.             if ( ent->s.event ) {
  1445.                 ent->s.event = 0;    // &= EV_EVENT_BITS;
  1446.                 if ( ent->client ) {
  1447.                     ent->client->ps.externalEvent = 0;
  1448.                     ent->client->ps.events[0] = 0;
  1449.                     ent->client->ps.events[1] = 0;
  1450.                 }
  1451.             }
  1452.             if ( ent->freeAfterEvent ) {
  1453.                 // tempEntities or dropped items completely go away after their event
  1454.                 G_FreeEntity( ent );
  1455.                 continue;
  1456.             } else if ( ent->unlinkAfterEvent ) {
  1457.                 // items that will respawn will hide themselves after their pickup event
  1458.                 ent->unlinkAfterEvent = qfalse;
  1459.                 trap_UnlinkEntity( ent );
  1460.             }
  1461.         }
  1462.  
  1463.         // temporary entities don't think
  1464.         if ( ent->freeAfterEvent ) {
  1465.             continue;
  1466.         }
  1467.  
  1468.         if ( !ent->r.linked && ent->neverFree ) {
  1469.             continue;
  1470.         }
  1471.  
  1472.         if ( ent->s.eType == ET_MISSILE ) {
  1473.             G_RunMissile( ent );
  1474.             continue;
  1475.         }
  1476.  
  1477.         if ( ent->s.eType == ET_ITEM || ent->physicsObject ) {
  1478.             G_RunItem( ent );
  1479.             continue;
  1480.         }
  1481.  
  1482.         if ( ent->s.eType == ET_MOVER ) {
  1483.             G_RunMover( ent );
  1484.             continue;
  1485.         }
  1486.  
  1487.         if ( i < MAX_CLIENTS ) {
  1488.             G_RunClient( ent );
  1489.             continue;
  1490.         }
  1491.  
  1492.         G_RunThink( ent );
  1493.     }
  1494. end = trap_Milliseconds();
  1495.  
  1496. start = trap_Milliseconds();
  1497.     // perform final fixups on the players
  1498.     ent = &g_entities[0];
  1499.     for (i=0 ; i < level.maxclients ; i++, ent++ ) {
  1500.         if ( ent->inuse ) {
  1501.             ClientEndFrame( ent );
  1502.         }
  1503.     }
  1504. end = trap_Milliseconds();
  1505.  
  1506.     // see if it is time to do a tournement restart
  1507.     CheckTournement();
  1508.  
  1509.     // see if it is time to end the level
  1510.     CheckExitRules();
  1511.  
  1512.     // update to team status?
  1513.     CheckTeamStatus();
  1514.  
  1515.     // cancel vote if timed out
  1516.     CheckVote();
  1517.  
  1518.     // for tracking changes
  1519.     CheckCvars();
  1520. }
  1521.